/*
 * Copyright (c) 2010, artur
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

/*
 * kiddie-os
 * Created on: 14.04.2010
 *     Author: artur
 *
 */

#include <irq/EventController.h>
#include <hw/Platform.h>

extern kernel::Platform hwplatform;
extern kernel::sys::sched::task::Task *current;

namespace kernel
{
  namespace irq
  {

    /**
     *
     */
    EventController::EventController()
    {
    }

    /**
     *
     */
    EventController::~EventController()
    {
    }

    void
    EventController::init(void)
    {
      archinit();
    }

    /**
     *
     */
    reg_t
    EventController::onInterrupt(struct cpu_regs *regs)
    {
      unsigned int ivecno = this->irq_translate(regs);
      this->irq_received(ivecno);
      EventListener *listener = this->irq_table[ivecno];
      if (listener)
        listener->onEvent(0);
    }

    /**
     *
     */
    reg_t
    EventController::onSyscall(struct cpu_regs *regs)
    {
      reg_t res = 0;
      unsigned int ivecno = this->irq_translate(regs);
      // hwplatform.console << "[ Controller ] onSyscall(" << (int)(((regs->trap_no))) << ")\n";
#ifdef IRQDEBUG
      hwplatform.console << kernel::Console::HEX;
      hwplatform.console << "0x" << (int)(((regs->eax))) << ";" << "0x" << (int)(((regs->ebx))) << ";";
      hwplatform.console << "0x" << (int)(((regs->ecx))) << ";" << "0x" << (int)(((regs->edx))) << ";\n";
      hwplatform.console << kernel::Console::DEC;
#endif
      if (regs->trap_no == 2)
        {
          res = ::current->vmap.remap(regs->ebx, regs->ecx);
          ::hwplatform._switch_mmu(current->vmap.pgd); // update new page map
        }
      if (regs->trap_no == 1)
        {
          ::hwplatform.console << (char) (regs->ebx);
        }

      return res;
    }

    /**
     *
     */
    reg_t
    EventController::onTrap(struct cpu_regs *regs)
    {
      unsigned int ivecno = this->irq_translate(regs);
      ::hwplatform.console << "--- BUG() ---\n" << ::kernel::Console::HEX;
      ::hwplatform.console << "[ Controller ] onTrap(" << (int) (((ivecno)))
          << ")\n";
      ::hwplatform.console << "[ Controller ] EIP: 0x" << (int) regs->eip
          << "\n";
      ::hwplatform.console << "[ Controller ] Cur->Entry: 0x"
          << (int) (((current->entry))) << "+0x" << (int) ((int) regs->eip
          - (int) current->entry) << "\n";

      ::hwplatform.console << "[ Controller ] CR2: 0x"
          << (int) ::hwplatform._get_data_abort_addr(regs) << ", ERR(0x"
          << (int) regs->err_code << ")\n" << ::kernel::Console::DEC;

      if (ivecno == kernel::irq::Event::PageFaultEvent)
        {
          ::current->vmap.mapPage((paddr_t) ::hwplatform.pageFactory.alloc(
              PAGE_SIZE, 0), (vaddr_t) PAGE_TRUNC(
              ::hwplatform._get_data_abort_addr(regs)));
          // flush tlb
          ::hwplatform._switch_mmu(current->vmap.pgd);
          return 0;
        }
      else
        while (1)
          ::hwplatform.idle();
    }

    /**
     *
     */
    EventController&
    EventController::operator <<(ONOFF mode)
    {
      switch (mode)
        {
      case irq::EventController::OFF:
        irq_disable();
        break;
      case irq::EventController::ON:
        irq_enable();
        break;
      case irq::EventController::INIT:
        init();
        break;
        }
      return *this;
    }

    /**
     *
     */
    bool
    EventController::AddEventListener(Event::EventType type,
        EventListener *listener)
    {
      if ((int) type > IRQ_ENTRIES || (int) type < 0)
        return false;
      this->irq_table[(int) (((type)))] = listener;
      return true;
    }

    /**
     *
     */
    EventController&
    EventController::operator[](kernel::irq::Event::EventType etype)
    {
      this->eventstate = etype;
      return *this;
    }

    /**
     *
     */
    EventController&
    EventController::operator =(EventListener *lstnr)
    {
      this->AddEventListener(this->eventstate, lstnr);
      return *this;
    }

  }
}
